#include "client.h"

#include <iostream>
#include <cstdlib>
#include <vector>
#include <cstring>

#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/socket.h>

Client::Client(std::string name)
{
  std::cout << "create new client named " << name << std::endl;
  client_name = name;
  sockfd = socket(AF_INET, SOCK_STREAM, 0); // fd is file descriptor
}


// typedef uint32_t in_addr_t
// typedef uint16_t in_port_t
bool Client::link(const char* localhost = (const char*) LOCALHOST, in_port_t port=SEVPORT)
{
  //std::cout << "enter function link" << std::endl;
  if(sockfd == -1)
  {
    std::cerr << "ERROR: request for socket fails!" << std::endl;
    std::cerr << ">> ";
    return false;
  }
  else
  {
    if(link_status == ESTABLISH)
    {
      std::cerr << "ERROR: already have link a server!" << std::endl;
      std::cerr << ">> ";
      return false;
    }
    remote_port = SEVPORT;
    struct sockaddr_in sev_addr;
    sev_addr.sin_family = AF_INET;
    sev_addr.sin_port = htons(remote_port);
    sev_addr.sin_addr.s_addr = inet_addr(localhost);
    memset(&(sev_addr.sin_zero),'\0', sizeof(sev_addr.sin_zero));
    //std::cout << "begin to connect server addr: " << sev_addr.sin_addr.s_addr << std::endl;
    int flag = connect(sockfd, (struct sockaddr *)&sev_addr, sizeof(struct sockaddr));
    if(flag == 0)
    {
      link_status = ESTABLISH;
      bool send_flag = send_msg(client_name); // send localhost's name directly
      std::cerr << ">> ";
      return send_flag;
    }
    else
    {
      std::cerr << "ERROR: unable to connect to server!" << std::endl;
      std::cerr << ">> ";
      return false;
    }
  }
}

bool Client::unlink()
{
  bool flag = false;
  if(link_status == UNESTABLISH)
  {
    flag = false;
    std::cerr << "unable to execute command because link is not established!" << std::endl;
    std::cerr << ">> ";
    return flag;
  }

  int shut_flag = shutdown(sockfd, SHUT_RDWR); // release the socket both side
  if(shut_flag == 0)
  {
    link_status = UNESTABLISH;
    flag = true;
  }
  else
  {
    if(link_status == UNESTABLISH)
    {
      std::cerr << "Error: unable to unlink because the link is unestablished!" << std::endl;
    }
    else
    {
      std::cerr << "Error: unable to unlink when link_status is ESTABLISHED" << std::endl;
    }
    flag = false;
  }
  std::cerr << ">> ";
  return flag;
}

bool Client::request_name()
{
  std::string msg = "n";
  bool flag = send_msg(msg);
  return flag;

  // if(flag == false)
  // {
  //   return flag;
  // }
  // else
  // {
  //   char recv_buf[BUFFER_SIZE];
  //   memset(recv_buf, '\0', BUFFER_SIZE);
  //   int recvbytes = recv(sockfd, recv_buf, BUFFER_SIZE, 0);
  //   if(recvbytes > 0)
  //   {
  //     std::string sname(recv_buf+2);
  //     server_name = sname;
  //     std::cout << "client " << client_name << " receives bytes: " << recvbytes << std::endl;
  //     std::cout << sname << std::endl;
  //     return flag;
  //   }
  //   else
  //   {
  //     std::cerr << "Error: unexpected net packet for server's name " << std::endl;
  //     return false;
  //   }
  // }
}

bool Client::request_time()
{
  std::string msg = "t";
  bool flag = send_msg(msg);
  return flag;
  // if(flag == false)
  // {
  //   return false;
  // }
  // else
  // {
  //   char recv_buf[BUFFER_SIZE];
  //   int recvbytes = recv(sockfd, recv_buf, BUFFER_SIZE, 0);
  //   if(recvbytes > 0)
  //   {
  //     std::string stime(recv_buf+2);
  //     std::cout << "client " << client_name << " receive bytes: " << recvbytes << std::endl;
  //     std::cout << stime << std::endl;
  //     return true;
  //   }
  //   else
  //   {
  //     std::cerr << "Error: unexpected net packet for request time!" << std::endl;
  //     return false;
  //   }
  // }
}

bool Client::request_info()
{
  //std::cerr << "enter function request_info" << std::endl;
  std::string msg = "i";
  bool flag = send_msg(msg);
  //std::cerr << "request_info flag = " << flag << std::endl;
  return flag;
  // if(flag == false)
  // {
  //   return false;
  // }
  // else
  // {
  //   char recv_buf[BUFFER_SIZE];
  //   memset(recv_buf, '\0', BUFFER_SIZE);
  //   int recvbytes = recv(sockfd, recv_buf, BUFFER_SIZE, 0);
  //   if (recvbytes > 0)
  //   {
  //     std::string sinfo(recv_buf+2);
  //     std::cout << "client " << client_name << " receive bytes: " << recvbytes << std::endl;
  //     std::cout <<  sinfo << std::endl;
  //     return true;
  //   }
  //   else
  //   {
  //     std::cerr << "ERROR: empty net packet for server's link info" << std::endl;
  //     return false;
  //   }
  // }
}


bool Client::request_forward(std::string name, std::string msg)
{
  std::string msg_pkt = "f " + name + " " + msg;
  bool flag = false;
  if(link_status == UNESTABLISH)
  {
    std::cerr << "ERROR: unable to execute command because link is not established!" << std::endl;
    std::cerr << ">> ";
    return false;
  }
  else if(msg.empty())
  {
    std::cerr << "ERROR: message forwarded should not be empty!" << std::endl;
    std::cerr << ">> ";
    return false;
  }
  else
  {
    flag = send_msg(msg_pkt);
    return flag;
  }

  //std::cout << "requst_forward flag = " << flag << std::endl;
  // if(flag == false)
  // {
  //   return false;
  // }
  // else
  // {
  //   char recv_buf[BUFFER_SIZE];
  //   int recvbytes = recv(sockfd, recv_buf, BUFFER_SIZE, 0);
  //   std::string sanswer(recv_buf+2);
  //   std::cout << "client " << client_name << "receive bytes: " << recvbytes << std::endl;
  //   std::cout << sanswer << std::endl;
  //   return flag;
  // }
}

bool Client::send_msg(std::string msg)
{
  //std::cerr << "enter function send_msg" << std::endl;
  const char* msg_p = msg.data();
  if(link_status == UNESTABLISH)
  {
    std::cerr << "ERROR: unable to execute command because link is not established!" << std::endl;
    std::cerr << ">> ";
    return false;
  }
  if(sockfd == -1)
  {
    std::cerr << "ERROR: unable to execute command because sockfd = -1!" << std::endl;
    std::cerr << ">> ";
    return false;
  }
  else
  {
    //std::cerr << "begin to send" << std::endl;
    int send_flag = send(sockfd, msg_p, msg.length(), 0);
    //std::cerr << "after send" << std::endl;
    if(send_flag == -1)
    {
      return false;
    }
    else
    {
      return true;
    }
  }
}

bool Client::quit()
{
  bool flag = false;
  if(link_status == UNESTABLISH)
  {
    flag = true;
    exit(0);
    return flag;
  }
  else
  {
    flag = unlink();
    exit(0);
    return flag;
  }
}

bool Client::help()
{
  // user interface guide
  std::cout << "COMMAND LINE INSTRUCTIONS LIST:" << std::endl;
  std::cout << "link [ADDRESS [PORT]]" << std::endl;
  std::cout << "unlink" << std::endl;
  std::cout << "time" << std::endl;
  std::cout << "name" << std::endl;
  std::cout << "info" << std::endl;
  std::cout << "forword NAME MESSAGE" << std::endl;
  std::cout << "quit" << std::endl;
  std::cout << std::endl << std::endl << "Please enter your command below" << std::endl;
  return true;
}


std::vector<std::string> Client::cmd_lexer(std::string cmd)
{

  std::string word_buffer;
  std::vector<std::string> token;
  //std::cout << "input command is " << cmd << std::endl;
  for(auto letter_p=cmd.begin(); letter_p!=cmd.end(); letter_p++)
  {
    //std::cout << "dealing with character " << (*letter_p) << std::endl;
    if(*letter_p == ' ')
    {
      //std::cout << "enter blank space" << std::endl;
      if(word_buffer == "")
      {
        continue;
      }
      else
      {
        //std::cout << "push back " << word_buffer << std::endl;
        token.push_back(word_buffer);
        word_buffer = "";
      }
    }
    else
    {
      word_buffer += (*letter_p);
      //std::cout << "now the word buffer = " << word_buffer << std::endl;
    }
  }
  if(!word_buffer.empty())
  {
    token.push_back(word_buffer);
  }

  return token;
}

bool Client::cmd_action(std::vector<std::string> token)
{
  bool flag = false;
  if(token.empty())
  {
    return true;
  }
  else
  {
    if(token[0] == "link"){
      if(token.size() == 1)
      {
        flag = this->link();
      }
      else
      {
        if(token.size() == 3)
        {
          std::string port_str(token[1]);
          std::string addr(token[2]);
          const char* port_p = port_str.data();
          const char* addr_p = addr.data();
          in_port_t port = atoi(port_p);
          flag = this->link(addr_p, port);
        }
        else
        {
          flag = false;
        }
      }
    }
    else if(token[0] == "unlink"){
      flag = this->unlink();
    }
    else if(token[0] == "name"){
      flag = this->request_name();
    }
    else if(token[0] == "time"){
      flag = this->request_time();
    }
    else if(token[0] == "info"){
      flag = this->request_info();
    }
    else if(token[0] == "quit"){
      flag = this->quit();
    }
    else if(token[0] == "forward"){
      if(token.size() == 1)
      {
        std::cerr << "ERROR: did not specify the client's name and message!" << std::endl;
        std::cerr << ">> ";
        flag = false;
      }
      else if(token.size() == 2)
      {
        std::cerr << "ERROR: message forwarded should not be empty!" << std::endl;
        flag = false;
      }
      else
      {
        std::string name(token[1]);
        std::string msg(token[2]);
        flag = this->request_forward(name, msg);
      }
    }
    else if(token[0] == "help"){
      flag = this->help();
    }
    else
    {
      std::cerr << "ERROR: command not found!" << std::endl;
      std::cerr << ">> ";
    }
  }
  return flag;
}

void cmd_handler(Client& clt)
{
  //int loop_cnt = 1;
  std::cout << ">> ";
  while(true)
  {
    //std::cerr << "this loop run " << loop_cnt << " times" << std::endl;
    std::string cmd;
    getline(std::cin, cmd);
    std::vector<std::string> token = clt.cmd_lexer(cmd);
    //std::cerr << "command token is " << std::endl;
    //for(auto token_p = token.begin(); token_p != token.end(); token_p++)
    //{
    //  std::cerr << *(token_p) << std::endl;
    //}
    clt.cmd_action(token);
    //loop_cnt++;
  }
}

void rcv_handler(Client& clt)
{
  char recv_buf[BUFFER_SIZE];
  memset(recv_buf, '\0', BUFFER_SIZE);
  while (true)
  {
    ssize_t bytes = recv(clt.sockfd, recv_buf, BUFFER_SIZE, 0);
    if(bytes > 0)
    {
      std::string msg(recv_buf);
      memset(recv_buf, '\0', BUFFER_SIZE);
      std::vector<std::string> token = clt.cmd_lexer(msg);
      //for(auto token_p = token.begin(); token_p != token.end(); token_p++)
      //{
      //  std::cerr << *(token_p) << std::endl;
      //}
      if(token.size() == 0)
      {
        std::cerr << "token.size == 0" << std::endl;
        std::cerr << ">> ";
        continue;
      }
      else if(token[0] == "t")
      {
        for(auto token_p = token.begin()+1; token_p != token.end(); token_p++)
        {
          std::cerr << *(token_p) << " ";
        }
        std::cerr << std::endl;
        std::cerr << ">> " ;
      }
      else if(token[0] == "n")
      {
        std::cerr << token[1] << std::endl;
        std::cerr << ">> ";
      }
      else if(token[0] == "i")
      {
        if((token.size()-1) % 2 == 1)
        {
          std::cerr << "ERROR: Message received does not follow (ID, name) pattern!" << std::endl;
          std::cerr << ">> ";
        }
        else
        {
          for(auto token_p = token.begin()+1; token_p != token.end(); token_p=token_p+2)
          {
            auto fd_p = token_p;
            auto name_p = token_p + 1;
            std::cerr << *(fd_p) << " " << *(name_p) << std::endl;
          }
          std::cerr << ">> ";
        }
      }
      else if(token[0] == "f")
      {
        std::cout << std::endl;
        if(token.size() < 2)
        {
          std::cerr << "Error: message forwarded has " << token.size() << " word which is too short!" << std::endl;
          std::cerr << ">> ";
        }
        else
        {
          std::cerr << "Receive message from client: " << token[1] << std::endl;
          for(auto token_p = token.begin()+1; token_p != token.end(); token_p++)
          {
            std::cerr << *(token_p) << std::endl;
          }
          std::cerr << ">> ";
        }

      }
      else if(token[0] == "s")
      {
        if(token.size() != 2)
        {
          std::cerr << "ERROR: unexpected net package s which not contains two segment!" << std::endl;
          std::cerr << ">> ";
        }
        else
        {
          std::cerr << "Send Message " << token[1] << "!" << std::endl;
        }
        std::cerr << ">> ";
      }
      else
      {
        std::cerr << "ERROR: net packet format unexpected!" << std::endl;
        std::cerr << ">> ";
      }
    }
  }
}
